package com.hilinos.im.core.longpoll;

import com.hilinos.common.common.ReturnCode;
import com.hilinos.common.util.EntityUtils;
import com.hilinos.im.core.ConnectorUtils;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import lombok.SneakyThrows;

public class ServerConnector {

    private static final AttributeKey<Long> TIMEOUT_KEY = AttributeKey.valueOf("timeout");
    private static final AttributeKey<String> USERID_KEY = AttributeKey.valueOf("userId");

    private static HttpResponse getEmptyResponse() {
        return getResponse("[]");
    }

    private static HttpResponse getResponse(String msg) {
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK,
                Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
        // 设置头信息
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
        return response;
    }

    /**
     * 响应options请求
     *
     * @return
     */
    private static HttpResponse responseOptions() {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        // 设置头信息
        response.headers()
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*")
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "*")
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST")
                .set(HttpHeaderNames.ACCESS_CONTROL_MAX_AGE, "3600")
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        return response;
    }

    private static final ConnectorUtils<Channel> CHANNELS = new ConnectorUtils<Channel>() {
        @Override
        public String getUseridByConnector(Channel connector) {
            try {
                return connector.attr(USERID_KEY).get();
            } catch (Exception e) {
                return null;
            }
        }

        @Override
        public void closeConnector(Channel connector) {
            try {
                connector.close();
            } catch (Exception ignored) {
            }
        }

        @Override
        public boolean needCloseConnector(Channel connector) {
            Long timeout = connector.attr(TIMEOUT_KEY).get();
            if (timeout == null || timeout < System.currentTimeMillis()) {
                connector.writeAndFlush(getEmptyResponse());
                return true;
            }
            return false;
        }

        @Override
        public void sendMsg(Channel connector, String msg) {
            connector.writeAndFlush(getResponse(msg));
        }
    };

    @SneakyThrows
    public static void main(String[] args) {
        ServerBootstrap bootstrap = new ServerBootstrap();
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup work = new NioEventLoopGroup();
        bootstrap.group(boss, work)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline()
                                .addLast(new HttpServerCodec())
                                .addLast("httpAggregator", new HttpObjectAggregator(512 * 1024))
                                .addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
                                    @Override
                                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                        super.channelActive(ctx);
                                        ctx.channel().attr(TIMEOUT_KEY).setIfAbsent(System.currentTimeMillis() + 25 * 1000);
                                        CHANNELS.addConnect(ctx.channel());
                                    }

                                    @Override
                                    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
                                        //处理跨域请求
                                        if (HttpMethod.OPTIONS.equals(req.method())) {
                                            ctx.channel().writeAndFlush(responseOptions());
                                            return;
                                        }

                                        // 获取请求的uri
                                        String uri = req.uri();
                                        String msg = EntityUtils.toJsonString(ReturnCode.SUCCESS.respMsg(uri));
                                        // 创建http响应
                                        FullHttpResponse response = new DefaultFullHttpResponse(
                                                HttpVersion.HTTP_1_1,
                                                HttpResponseStatus.OK,
                                                Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
                                        // 设置头信息
                                        response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
                                        // 将html write到客户端
                                        ctx.channel().writeAndFlush(response);
                                    }
                                });
                    }
                });
        ChannelFuture f = bootstrap.bind(18898).sync();
        System.out.println(" server start up on port : " + 18898);
        f.channel().closeFuture().sync();
    }

}
